/*
*********************************************************************************************************
*                                          Analog I/O Module
*
*                            (c) Copyright 1999, Jean J. Labrosse, Weston, FL
*                                          All Rights Reserved
*
* Filename   : AIO.C
* Programmer : Jean J. Labrosse
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                           INCLUDE FILES
*********************************************************************************************************
*/

#define   AIO_GLOBALS
#include "includes.h"

/*
*********************************************************************************************************
*                                          LOCAL VARIABLES
*********************************************************************************************************
*/

static  OS_STK      AIOTaskStk[AIO_TASK_STK_SIZE];
static  OS_EVENT   *AIOSem;

/*
*********************************************************************************************************
*                                      LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/

        void        AIOTask(void *data);

static  void        AIInit(void);
static  void        AIUpdate(void);

static  void        AOInit(void);
static  void        AOUpdate(void);

/*$PAGE*/
/*
*********************************************************************************************************
*                 CONFIGURE THE CALIBRATION PARAMETERS OF AN ANALOG INPUT CHANNEL
*
* Description : This function is used to configure an analog input channel.
* Arguments   : n        is the analog input channel to configure:
*               gain     is the calibration gain
*               offset   is the calibration offset
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog input channel number.
*********************************************************************************************************
*/

INT8U  AICfgCal (INT8U n, FP32 gain, FP32 offset)
{
    INT8U err;
    AIO  *paio;


    if (n < AIO_MAX_AI) {
        paio               = &AITbl[n];               /* Point to Analog Input structure               */
        OSSemPend(AIOSem, 0, &err);                   /* Obtain exclusive access to AI channel         */
        paio->AIOCalGain   = gain;                    /* Store new cal. gain and offset into struct    */
        paio->AIOCalOffset = offset;
        paio->AIOGain      = paio->AIOCalGain   * paio->AIOConvGain;      /* Compute overall gain      */
        paio->AIOOffset    = paio->AIOCalOffset + paio->AIOConvOffset;    /* Compute overall offset    */
        OSSemPost(AIOSem);                                                /* Release AI channel        */
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                    CONFIGURE THE CONVERSION PARAMETERS OF AN ANALOG INPUT CHANNEL
*
* Description : This function is used to configure an analog input channel.
* Arguments   : n        is the analog channel to configure (0..AIO_MAX_AI-1).
*               gain     is the conversion gain
*               offset   is the conversion offset
*               pass     is the value for the pass counts
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog input channel number.
*********************************************************************************************************
*/

INT8U  AICfgConv (INT8U n, FP32 gain, FP32 offset, INT8U pass)
{
    INT8U err;
    AIO  *paio;


    if (n < AIO_MAX_AI) {
        paio                = &AITbl[n];              /* Point to Analog Input structure               */
        OSSemPend(AIOSem, 0, &err);                   /* Obtain exclusive access to AI channel         */
        paio->AIOConvGain   = gain;                   /* Store new conv. gain and offset into struct   */
        paio->AIOConvOffset = offset;
        paio->AIOGain       = paio->AIOCalGain   * paio->AIOConvGain;     /* Compute overall gain      */
        paio->AIOOffset     = paio->AIOCalOffset + paio->AIOConvOffset;   /* Compute overall offset    */
        paio->AIOPassCnts   = pass;
        OSSemPost(AIOSem);                                                /* Release AI channel        */
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                      CONFIGURE THE SCALING PARAMETERS OF AN ANALOG INPUT CHANNEL
*
* Description : This function is used to configure the scaling parameters associated with an analog
*               input channel.
* Arguments   : n        is the analog input channel to configure (0..AIO_MAX_AI-1).
*               arg      is a pointer to arguments needed by the scaling function
*               fnct     is a pointer to a scaling function
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog input channel number.
*********************************************************************************************************
*/

INT8U  AICfgScaling (INT8U n, void (*fnct)(AIO *paio), void *arg)
{
    AIO *paio;


    if (n < AIO_MAX_AI) {
        paio                  = &AITbl[n];            /* Faster to use a pointer to the structure      */
        OS_ENTER_CRITICAL();
        paio->AIOScaleFnct    = (void (*)())fnct;
        paio->AIOScaleFnctArg = arg;
        OS_EXIT_CRITICAL();
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                GET THE VALUE OF AN ANALOG INPUT CHANNEL
*
* Description : This function is used to get the currect value of an analog input channel (in engineering
*               units).
* Arguments   : n     is the analog input channel (0..AIO_MAX_AI-1).
*               pval  is a pointer to the destination engineering units of the analog input channel
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog input channel number.
*                        In this case, the destination is not changed.
*********************************************************************************************************
*/

INT8U  AIGet (INT8U n, FP32 *pval)
{
    AIO  *paio;


    if (n < AIO_MAX_AI) {
        paio  = &AITbl[n];
        OS_ENTER_CRITICAL();           /* Obtain exclusive access to AI channel                        */
        *pval = paio->AIOEU;           /* Get the engineering units of the analog input channel        */
        OS_EXIT_CRITICAL();            /* Release AI channel                                           */
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                     ANALOG INPUTS INITIALIZATION
*
* Description : This function initializes the analog input channels.
* Arguments   : None
* Returns     : None.
*********************************************************************************************************
*/

static  void  AIInit (void)
{
    INT8U   i;
    AIO    *paio;


    paio = &AITbl[0];
    for (i = 0; i < AIO_MAX_AI; i++) {
        paio->AIOBypassEn     =  FALSE;           /* Analog channel is not bypassed                     */
        paio->AIORaw          =  0x0000;          /* Raw counts of ADC or DAC                           */
        paio->AIOEU           = (FP32)0.0;        /* Engineering units of AI channel                    */
        paio->AIOGain         = (FP32)1.0;        /* Total gain                                         */
        paio->AIOOffset       = (FP32)0.0;        /* Total offset                                       */
        paio->AIOLim          =       0;
        paio->AIOPassCnts     =       1;          /* Pass counts                                        */
        paio->AIOPassCtr      =       1;          /* Pass counter                                       */
        paio->AIOCalGain      = (FP32)1.0;        /* Calibration gain                                   */
        paio->AIOCalOffset    = (FP32)0.0;        /* Calibration offset                                 */
        paio->AIOConvGain     = (FP32)1.0;        /* Conversion gain                                    */
        paio->AIOConvOffset   = (FP32)0.0;        /* Conversion offset                                  */
        paio->AIOScaleIn      = (FP32)0.0;        /* Input  to scaling function                         */
        paio->AIOScaleOut     = (FP32)0.0;        /* Output of scaling function                         */
        paio->AIOScaleFnct    = (void *)0;        /* No function to execute                             */
        paio->AIOScaleFnctArg = (void *)0;        /* No arguments to scale function                     */
        paio++;
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                   ANALOG I/O MANAGER INITIALIZATION
*
* Description : This function initializes the analog I/O manager module.
* Arguments   : None
* Returns     : None.
*********************************************************************************************************
*/

void  AIOInit (void)
{
    INT8U   err;


    AIInit();
    AOInit();
    AIOInitIO();
    AIOSem = OSSemCreate(1);                     /* Create a mutual exclusion semaphore for AIOs       */
    OSTaskCreateExt(AIOTask, (void *)0, &AIOTaskStk[AIO_TASK_STK_SIZE], AIO_TASK_PRIO,
                    AIO_TASK_PRIO, &AIOTaskStk[0], AIO_TASK_STK_SIZE, (void *)0, OS_TASK_OPT_SAVE_FP);
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                      ANALOG I/O MANAGER TASK
*
* Description : This task is created by AIOInit() and is responsible for updating the analog inputs and
*               analog outputs.
*               AIOTask() executes every AIO_TASK_DLY milliseconds.
* Arguments   : None.
* Returns     : None.
*********************************************************************************************************
*/

void  AIOTask (void *data)
{
    INT8U err;


    data = data;                                 /* Avoid compiler warning                             */
    for (;;) {
        OSTimeDlyHMSM(0, 0, 0, AIO_TASK_DLY);    /* Delay between execution of AIO manager             */

        OSSemPend(AIOSem, 0, &err);              /* Obtain exclusive access to AI channels             */
        AIUpdate();                              /* Update all AI channels                             */
        OSSemPost(AIOSem);                       /* Release AI channels (Allow high prio. task to run) */

        OSSemPend(AIOSem, 0, &err);              /* Obtain exclusive access to AO channels             */
        AOUpdate();                              /* Update all AO channels                             */
        OSSemPost(AIOSem);                       /* Release AO channels (Allow high prio. task to run) */
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                           SET THE STATE OF THE BYPASSED ANALOG INPUT CHANNEL
*
* Description : This function is used to set the engineering units of a bypassed analog input channel.
*               This function is used to simulate the presense of the sensor.  This function is only
*               valid if the bypass 'switch' is open.
* Arguments   : n     is the analog input channel (0..AIO_MAX_AI-1).
*               val   is the value of the bypassed analog input channel:
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog input channel number.
*               2        if AIOBypassEn was not set to TRUE
*********************************************************************************************************
*/

INT8U  AISetBypass (INT8U n, FP32 val)
{
    AIO   *paio;


    if (n < AIO_MAX_AI) {
        paio = &AITbl[n];                        /* Faster to use a pointer to the structure           */
        if (paio->AIOBypassEn == TRUE) {         /* See if the analog input channel is bypassed        */
            OS_ENTER_CRITICAL();
            paio->AIOEU = val;                   /* Yes, then set the new value of the channel         */
            OS_EXIT_CRITICAL();
            return (0);
        } else {
            return (2);
        }
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                  SET THE STATE OF THE BYPASS SWITCH
*
* Description : This function is used to set the state of the bypass switch.  The analog input channel is
*               bypassed when the 'switch' is open (i.e. AIOBypassEn is set to TRUE).
* Arguments   : n      is the analog input channel (0..AIO_MAX_AI-1).
*               state  is the state of the bypass switch:
*                         FALSE disables the bypass (i.e. the bypass 'switch' is closed)
*                         TRUE  enables  the bypass (i.e. the bypass 'switch' is open)
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog input channel number.
*********************************************************************************************************
*/

INT8U  AISetBypassEn (INT8U n, BOOLEAN state)
{
    if (n < AIO_MAX_AI) {
        AITbl[n].AIOBypassEn = state;
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                   UPDATE ALL ANALOG INPUT CHANNELS
*
* Description : This function processes all of the analog input channels.
* Arguments   : None.
* Returns     : None.
*********************************************************************************************************
*/

static  void  AIUpdate (void)
{
    INT8U   i;
    AIO    *paio;


    paio = &AITbl[0];                            /* Point at first analog input channel                */
    for (i = 0; i < AIO_MAX_AI; i++) {           /* Process all analog input channels                  */
        if (paio->AIOBypassEn == FALSE) {        /* See if analog input channel is bypassed            */
            paio->AIOPassCtr--;                  /* Decrement pass counter                             */
            if (paio->AIOPassCtr == 0) {         /* When pass counter reaches 0, read and scale AI     */
                paio->AIOPassCtr = paio->AIOPassCnts;                /* Reload pass counter            */
                paio->AIORaw     = AIRd(i);                          /* Read ADC for this channel      */
                paio->AIOScaleIn = ((FP32)paio->AIORaw + paio->AIOOffset) * paio->AIOGain;
                if ((void *)paio->AIOScaleFnct != (void *)0) {       /* See if function defined        */
                    (*paio->AIOScaleFnct)(paio);                     /* Yes, execute function          */
                } else {
                    paio->AIOScaleOut = paio->AIOScaleIn;            /* No,  just copy data            */
                }
                paio->AIOEU = paio->AIOScaleOut;                     /* Output of scaling fnct to E.U. */
            }
        }
        paio++;                                                      /* Point at next AI channel       */
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                 CONFIGURE THE CALIBRATION PARAMETERS OF AN ANALOG OUTPUT CHANNEL
*
* Description : This function is used to configure an analog output channel.
* Arguments   : n        is the analog output channel to configure (0..AIO_MAX_AO-1)
*               gain     is the calibration gain
*               offset   is the calibration offset
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog output channel number.
*********************************************************************************************************
*/

INT8U  AOCfgCal (INT8U n, FP32 gain, FP32 offset)
{
    INT8U  err;
    AIO   *paio;


    if (n < AIO_MAX_AO) {
        paio               = &AOTbl[n];               /* Point to Analog Output structure              */
        OSSemPend(AIOSem, 0, &err);                   /* Obtain exclusive access to AO channel         */
        paio->AIOCalGain   = gain;                    /* Store new cal. gain and offset into struct    */
        paio->AIOCalOffset = offset;
        paio->AIOGain      = paio->AIOCalGain   * paio->AIOConvGain;      /* Compute overall gain      */
        paio->AIOOffset    = paio->AIOCalOffset + paio->AIOConvOffset;    /* Compute overall offset    */
        OSSemPost(AIOSem);                                                /* Release AO channel        */
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                    CONFIGURE THE CONVERSION PARAMETERS OF AN ANALOG OUTPUT CHANNEL
*
* Description : This function is used to configure an analog output channel.
* Arguments   : n        is the analog channel to configure (0..AIO_MAX_AO-1).
*               gain     is the conversion gain
*               offset   is the conversion offset
*               pass     is the value for the pass counts
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog output channel number.
*********************************************************************************************************
*/

INT8U  AOCfgConv (INT8U n, FP32 gain, FP32 offset, INT16S lim, INT8U pass)
{
    INT8U err;
    AIO  *paio;


    if (n < AIO_MAX_AO) {
        paio                = &AOTbl[n];              /* Point to Analog Output structure              */
        OSSemPend(AIOSem, 0, &err);                   /* Obtain exclusive access to AO channel         */
        paio->AIOConvGain   = gain;                   /* Store new conv. gain and offset into struct   */
        paio->AIOConvOffset = offset;
        paio->AIOGain       = paio->AIOCalGain   * paio->AIOConvGain;     /* Compute overall gain      */
        paio->AIOOffset     = paio->AIOCalOffset + paio->AIOConvOffset;   /* Compute overall offset    */
        paio->AIOLim        = lim;
        paio->AIOPassCnts   = pass;
        OSSemPost(AIOSem);                                                /* Release AO channel        */
        return (0);
    } else {
        return (1);
    }
}
/*$PAGE*/
/*
*********************************************************************************************************
*                      CONFIGURE THE SCALING PARAMETERS OF AN ANALOG OUTPUT CHANNEL
*
* Description : This function is used to configure the scaling parameters associated with an analog
*               output channel.
* Arguments   : n        is the analog output channel to configure (0..AIO_MAX_AO-1).
*               arg      is a pointer to arguments needed by the scaling function
*               fnct     is a pointer to a scaling function
* Returns     : 0        if successfull.
*               1        if you specified an invalid analog output channel number.
*********************************************************************************************************
*/

INT8U  AOCfgScaling (INT8U n, void (*fnct)(AIO *paio), void *arg)
{
    AIO *paio;


    if (n < AIO_MAX_AO) {
        paio                  = &AOTbl[n];            /* Faster to use a pointer to the structure      */
        OS_ENTER_CRITICAL();
        paio->AIOScaleFnct    = (void (*)())fnct;
        paio->AIOScaleFnctArg = arg;
        OS_EXIT_CRITICAL();
        return (0);
    } else {
        return (1);
    }
}
/*$PAGE*/
/*
*********************************************************************************************************
*                                     ANALOG OUTPUTS INITIALIZATION
*
* Description : This function initializes the analog output channels.
* Arguments   : None
* Returns     : None.
*********************************************************************************************************
*/

static  void  AOInit (void)
{
    INT8U   i;
    AIO    *paio;


    paio = &AOTbl[0];
    for (i = 0; i < AIO_MAX_AO; i++) {
        paio->AIOBypassEn     = FALSE;           /* Analog channel is not bypassed                     */
        paio->AIORaw          = 0x0000;          /* Raw counts of ADC or DAC                           */
        paio->AIOEU           =  (FP32)0.0;      /* Engineering units of AI channel                    */
        paio->AIOGain         =  (FP32)1.0;      /* Total gain                                         */
        paio->AIOOffset       =  (FP32)0.0;      /* Total offset                                       */
        paio->AIOLim          =      0;          /* Maximum count of an analog output channel          */
        paio->AIOPassCnts     =      1;          /* Pass counts                                        */
        paio->AIOPassCtr      =      1;          /* Pass counter                                       */
        paio->AIOCalGain      =  (FP32)1.0;      /* Calibration gain                                   */
        paio->AIOCalOffset    =  (FP32)0.0;      /* Calibration offset                                 */
        paio->AIOConvGain     =  (FP32)1.0;      /* Conversion gain                                    */
        paio->AIOConvOffset   =  (FP32)0.0;      /* Conversion offset                                  */
        paio->AIOScaleIn      =  (FP32)0.0;      /* Input  to scaling function                         */
        paio->AIOScaleOut     =  (FP32)0.0;      /* Output of scaling function                         */
        paio->AIOScaleFnct    = (void *)0;       /* No function to execute                             */
        paio->AIOScaleFnctArg = (void *)0;       /* No arguments to scale function                     */
        paio++;
    }
}
/*$PAGE*/
/*
*********************************************************************************************************
*                                SET THE VALUE OF AN ANALOG OUTPUT CHANNEL
*
* Description : This function is used to set the currect value of an analog output channel
*               (in engineering units).
* Arguments   : n     is the analog output channel (0..AIO_MAX_AO-1).
*               val   is the desired analog output value in Engineering Units
* Returns     : 0     if successfull.
*               1     if you specified an invalid analog output channel number.
*********************************************************************************************************
*/

INT8U  AOSet (INT8U n, FP32 val)
{
    if (n < AIO_MAX_AO) {
        OS_ENTER_CRITICAL();
        AOTbl[n].AIOEU = val;          /* Set the engineering units of the analog output channel       */
        OS_EXIT_CRITICAL();
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                           SET THE STATE OF THE BYPASSED ANALOG OUTPUT CHANNEL
*
* Description : This function is used to set the engineering units of a bypassed analog output channel.
* Arguments   : n     is the analog output channel (0..AIO_MAX_AO-1).
*               val   is the value of the bypassed analog output channel:
* Returns     : 0     if successfull.
*               1     if you specified an invalid analog output channel number.
*               2     if AIOBypassEn is not set to TRUE
*********************************************************************************************************
*/

INT8U  AOSetBypass (INT8U n, FP32 val)
{
    AIO *paio;


    if (n < AIO_MAX_AO) {
        paio = &AOTbl[n];                        /* Faster to use a pointer to the structure           */
        if (paio->AIOBypassEn == TRUE) {         /* See if the analog output channel is bypassed       */
            OS_ENTER_CRITICAL();
            paio->AIOScaleIn = val;              /* Yes, then set the new value of the channel         */
            OS_EXIT_CRITICAL();
            return (0);
        } else {
            return (2);
        }
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                  SET THE STATE OF THE BYPASS SWITCH
*
* Description : This function is used to set the state of the bypass switch.  The analog output channel
*               is bypassed when the 'switch' is open (i.e. AIOBypassEn is set to TRUE).
* Arguments   : n      is the analog output channel (0..AIO_MAX_AO-1).
*               state  is the state of the bypass switch:
*                         FALSE disables the bypass (i.e. the bypass 'switch' is closed)
*                         TRUE  enables  the bypass (i.e. the bypass 'switch' is open)
* Returns     : 0      if successfull.
*               1      if you specified an invalid analog output channel number.
*********************************************************************************************************
*/

INT8U  AOSetBypassEn (INT8U n, BOOLEAN state)
{
    INT8U err;


    if (n < AIO_MAX_AO) {
        AOTbl[n].AIOBypassEn = state;
        return (0);
    } else {
        return (1);
    }
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                   UPDATE ALL ANALOG OUTPUT CHANNELS
*
* Description : This function processes all of the analog output channels.
* Arguments   : None.
* Returns     : None.
*********************************************************************************************************
*/

static  void  AOUpdate (void)
{
    INT8U     i;
    AIO      *paio;
    INT16S    raw;


    paio = &AOTbl[0];                            /* Point at first analog output channel               */
    for (i = 0; i < AIO_MAX_AO; i++) {           /* Process all analog output channels                 */
        if (paio->AIOBypassEn == FALSE) {        /* See if analog output channel is bypassed           */
            paio->AIOScaleIn = paio->AIOEU;      /* No                                                 */
        }
        paio->AIOPassCtr--;                      /* Decrement pass counter                             */
        if (paio->AIOPassCtr == 0) {             /* When pass counter reaches 0, read and scale AI     */
            paio->AIOPassCtr = paio->AIOPassCnts;          /* Reload pass counter                      */
            if ((void *)paio->AIOScaleFnct != (void *)0) { /* See if function defined                  */
                (*paio->AIOScaleFnct)(paio);               /* Yes, execute function                    */
            } else {
                paio->AIOScaleOut = paio->AIOScaleIn;      /* No,  bypass scaling function             */
            }
            raw = (INT16S)(paio->AIOScaleOut * paio->AIOGain + paio->AIOOffset);
            if (raw > paio->AIOLim) {                      /* Never output > maximum DAC counts        */
                raw = paio->AIOLim;
            } else if (raw < 0) {                          /* DAC counts must always be >= 0           */
                raw = 0;
            }
            paio->AIORaw = raw;
            AOWr(i, paio->AIORaw);                         /* Write counts to DAC                      */
        }
        paio++;                                            /* Point at next AO channel                 */
    }
}

/*$PAGE*/
#ifndef CFG_C
/*
*********************************************************************************************************
*                                      INITIALIZE PHYSICAL I/Os
*
* Description : This function is called by AIOInit() to initialize the physical I/O used by the AIO
*               driver.
* Arguments   : None.
* Returns     : None.
*********************************************************************************************************
*/

void  AIOInitIO (void)
{
    /* This is where you will need to put you initialization code for the ADCs and DACs                */
    /* You should also consider initializing the contents of your DAC(s) to a known value.             */
}

/*
*********************************************************************************************************
*                                         READ PHYSICAL INPUTS
*
* Description : This function is called to read a physical ADC channel.  The function is assumed to
*               also control a multiplexer if more than one analog input is connected to the ADC.
* Arguments   : ch     is the ADC logical channel number (0..AIO_MAX_AI-1).
* Returns     : The raw ADC counts from the physical device.
*********************************************************************************************************
*/

INT16S  AIRd (INT8U ch)
{
    /* This is where you will need to provide the code to read your ADC(s).                            */
    /* AIRd() is passed a 'LOGICAL' channel number.  You will have to convert this logical channel     */
    /* number into actual physical port locations (or addresses) where your MUX. and ADCs are located. */
    /* AIRd() is responsible for:                                                                      */
    /*     1) Selecting the proper MUX. channel,                                                       */
    /*     2) Waiting for the MUX. to stabilize,                                                       */
    /*     3) Starting the ADC,                                                                        */
    /*     4) Waiting for the ADC to complete its conversion,                                          */
    /*     5) Reading the counts from the ADC and,                                                     */
    /*     6) Returning the counts to the calling function.                                            */

    return (ch);
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                       UPDATE PHYSICAL OUTPUTS
*
* Description : This function is called to write the 'raw' counts to the proper analog output device
*               (i.e. DAC).  It is up to this function to direct the DAC counts to the proper DAC if more
*               than one DAC is used.
* Arguments   : ch     is  the DAC logical channel number (0..AIO_MAX_AO-1).
*               cnts   are the DAC counts to write to the DAC
* Returns     : None.
*********************************************************************************************************
*/

void  AOWr (INT8U ch, INT16S cnts)
{
    ch   = ch;
    cnts = cnts;

    /* This is where you will need to provide the code to update your DAC(s).                          */
    /* AOWr() is passed a 'LOGICAL' channel number.  You will have to convert this logical channel     */
    /* number into actual physical port locations (or addresses) where your DACs are located.          */
    /* AOWr() is responsible for writing the counts to the selected DAC based on a logical number.     */
}
#endif
